/*
 Copyright (c) Whitemice Consulting, 2007, 2008, 2009
 Adam Tauno Williams <awilliam@whitemice.org>
 License: MIT/X11

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using Gtk;
using Gdk;

namespace Whitemice.Calendar
{ 
  /// <remarks>
  /// The CalendarDay class is used by the DayStrip class to manage the 
  /// drawing of an individual day.
  /// </remarks>
  public class CalendarDay
  {
    private   DateTime              day;
    private   Rectangle             rectangle;
    
    public CalendarDay()
    {
      Init(DateTime.Now, 0, 0, 0, 0);
    }
    
    public CalendarDay(DateTime _day, int _x, int _y, int _width, int _height)
    {
      Init(_day, _x, _y, _width, _height);
    }
    
    public void Init(DateTime _day, int _x, int _y, int _width, int _height)
    {
      day = _day;
      rectangle = new Rectangle (_x, _y, _width, _height);
    }
    
    public int Padding
    {
      get { return 2; }
    }
    
    /// <summary>
    /// Returns the GUI rectangle used to constrain the day.
    /// </summary>
    /// <returns>
    /// A <see cref="Gdk.Rectangle"/>
    /// </returns>
    public Gdk.Rectangle Rectangle
    { get { return rectangle; } }
    
    /// <summary>
    /// Returns the day the object represents.
    /// </summary>
    /// <returns>
    /// A <see cref="System.DateTime"/>
    /// </returns>
    public DateTime Date
    { get { return day; } }
    
    /// <summary>
    /// Returns the week day name of the day the object represents.
    /// </summary>
    /// <returns>
    /// A <see cref="System.String"/>
    /// </returns>    
    public string DayName
    { get { return day.DayOfWeek.ToString(); } }
            
    /// <summary>
    /// Checks if the event specified occurs on the day represented by
    /// the object.
    /// </summary>
    /// <param name="_event">
    /// A <see cref="CalendarEvent"/>
    /// </param>
    /// <returns>
    /// A <see cref="System.Boolean"/>
    /// </returns>
    public bool HasEvent(CalendarEvent _event)
    {
      DateTime today = day.Date;
      DateTime tomorrow = today.AddDays(1.0);
      
      /* If the end of the appointment is greater than 
         the start of the day,  AND the start of the 
         appointment is less than the end of the day. */
      if ((_event.Start.CompareTo(tomorrow) < 0) && 
          (_event.End.CompareTo(today) > 0))
        return true;
      return false;
    } // end HasEvent 
    
    /// <summary>
    /// Paint the events onto the day,  it is assumed that any 
    /// calendar decorations such as hour lines or day boundaries
    /// have already been painted by the DayStrip.  The geometry
    /// and placement is calculated for each event and then the
    /// event's Paint method is called and provided with that
    /// geometry.  Events have a minimum height of 21p.
    /// </summary>
    /// <param name="_style">
    /// A <see cref="Gdk.Style"/>
    /// </param>
    /// <param name="_widget">
    /// A <see cref="Gtk.Widget"/>
    /// </param>
    /// <param name="_increment">
    /// A <see cref="System.Int32"/>
    /// </param>
    /// <param name="_events">
    /// A <see cref="System.Collections.Generic.IList"/>
    /// The list of candidate event,  only the events in this collection that
    /// occur on the appropriate day will be painted onto the day.
    /// </param>
    public void Paint(Style _style, Widget _widget, int _increment, IList<CalendarEvent> _events)
    {
      int                      x, y, w, h, c, d, t;
      List<CalendarEvent>      events, overlaps;
      DateTime                 start, end, tomorrow, today;
      
      events = new List<CalendarEvent>();
      overlaps = new List<CalendarEvent>();
      /* Of all the appointments we have just get the ones that occur on this
         day */
      foreach (CalendarEvent ev in _events)
        if (HasEvent(ev))
          events.Add(ev);
      /* bail out if the day ends up containing no events */
      if (events.Count > 0)
      {
        /* Ok, today has events! */
        tomorrow = this.Date.AddDays(1.0);
        today = this.Date;
        /* We need to process events that appear on this day based upong thier
           start time so we can layout overlapped appointments sanely, earlier
           starting appointments go the left when they overlap with other
           appointments.  Effectively we divide the day into columns */
        events.Sort();
        /* Process each event */
        foreach(CalendarEvent calEvent in events)
        {
          /* If the day overruns today (this day) then crop it to be just today */
          if (calEvent.Start.CompareTo(today) >= 0)
            start = calEvent.Start;
          else start = today;
          if (calEvent.End.CompareTo(tomorrow) <= 0)
            end = calEvent.End;
          else end = tomorrow;
          /* Calculate vertical start */
          y = Convert.ToInt32((start.Subtract(today).TotalMinutes / 15.0) * _increment);
          /* Calculate height */
          d = Convert.ToInt32(end.Subtract(start).TotalMinutes);
          h = Convert.ToInt32((d / 15.0) * _increment);
          if (h < 21) h = 21; 
          /* Calculate horizontal placement,  baseline is obviously the left
             side of the day + Padding; this involves find and sorting all
             the overlapping events. */ 
          overlaps = calEvent.FilterOverlapsInDay(_events, today);
          if (overlaps.Count > 1)
          {
            overlaps.Sort();
            /* Calculate maximum depth of overlaps, c will the the appropriate
               column for this appointment and d will be the number of 
               columns */
            d = c = t = 0;
            for(int i = 0; i < overlaps.Count; i++)
            {
              if (overlaps[i] == calEvent) 
              {
                c = i;
              } else
                {
                  t = overlaps[i].FilterOverlapsInWindow(overlaps, overlaps[i].Start, overlaps[i].End).Count;
                  Console.WriteLine("Overlap depth of {0} at {1} - {2}", t, overlaps[i].Start, overlaps[i].End);
                  if (t > d) d = t;
                }
            } /* end for-loop-overlapping-events */
            Console.WriteLine("chosen an overlap depth of {0}", d);
            w = ((Rectangle.Width - (Padding * (d - 1))) / d);
            x = (Rectangle.X + (Padding * (c + 1)) + (w * c));
          } else 
            {
              x = (Rectangle.X + Padding);
              w = (Rectangle.Width - (Padding * 2));
            } /* else event has no overlapping events */
          calEvent.Paint(_style, _widget, this, new Gdk.Rectangle(x, y, w, h));
          Console.WriteLine("-------------------------");
        } /* end foreach-all-calendar-events */
      }  /* end if-day-has-events (this day had no events) */
    } /* end Paint */
  } /* end CalendarDay */
}
